﻿using System;

using System.Collections.Generic;
using System.Text;
using Silvermoon.Controls;
using Silvermoon.Controls.Classes;
using System.Drawing;
using Silvermoon.Shapes;
using Silvermoon.Animations;
using Silvermoon.OpenGL;

using Silvermoon.Utils;
using Silvermoon;
using System.Threading;
using Silvermoon.Images;
using Testing;
using Silvermoon.UI;
using Silvermoon.Windows.Styles;
using Silvermoon.Windows;

namespace FileExplorer
{
    /// <summary>
    /// An animated calculator demo.
    /// Note that the functionality of the logic is not complete since it is only for demonstration purpose to expose how to create animations.
    /// </summary>
    public class Calculator : DemoWindowBase
    {
        #region fields
        private static GridDefinition buttonGridDef;
        private static GridDefinition startButtonState;
        private static GridDefinition infoGridDef;
        private static GridDefinition outerGridDef;

        private double amplitude = 25d;
        private List<CalcButton> buttons = new List<CalcButton>();
        private RotateTransform rotate;
        private AnimationGrid buttonGrid;
        private AnimationGrid infoGrid;
        private AnimationGrid outerGrid;
        private TextBox numberBox;
        private TextBox outputBox;

        private double calc = 0f;
        private double current = 0f;
        private Operation operation = Operation.Set;
        private bool clear = true;
        #endregion
        #region enums and constants

        const int DefaultAngle = 0 * 20;

        enum Operation
        {
            Set,
            Plus,
            Minus,
            Multiply,
            Divide
        }

        readonly int[] Angles = new int[] { 25, 20, 12, 15, 10, 15, 20, 15, 8, 0, 8, 12, 23, 19, 3, 5, 14, 18, 23, 20, 12, 15, 19, 25 };


        readonly string[] buttonsText = new string[]
        {
            "C","7","8","9","÷","1/x",
            "MC","4","5","6","x","%",
            "MR","1","2","3","-","√",
            "M+","0",",","=","+","±"
        };

        readonly int[] colorIndexes = new int[]
        {
            2,0,0,0,1,0,
            1,0,0,0,1,0,
            1,0,0,0,1,0,
            1,0,0,3,1,0
        };

        readonly Color[] buttonColors = new Color[]
        {
           //Color.FromArgb(24,24,24),
            Color.Black,
            Color.FromArgb(64,64,64),
            Color.DarkRed,
            Color.DarkGreen
        };


        readonly Color[] textColors = new Color[]
        {
            Color.White,
            Color.White,
            Color.White,
            Color.White
        };

        #endregion
        #region ctors

        /// <summary>
        /// We create only one grid definition and states for each instance to reduce memory.
        /// </summary>
        static Calculator()
        {
            buttonGridDef = new GridDefinition();
            buttonGridDef.Rows.AddRange(1f, 1f, 1f, 1f);
            buttonGridDef.Columns.AddRange(1f, 1f, 1f, 1f, 1f, 1f);
            buttonGridDef.CreateAutoCells();

            startButtonState = new GridDefinition();
            startButtonState.Rows.AddRange(5f, 1f);
            startButtonState.Columns.AddRange(5f, 1f, 5f);
            bool rev = true;
            Random r = new Random();
            for (int i = 0; i <= 4 * 6; i++)
            {
                rev = r.Next(2) == 1;
                GridCell cell = startButtonState.CreateCell(1, 1);
                if (rev) cell.RotateY = 270; else cell.RotateX = 270;
                cell.Alpha = 160;
            }

            infoGridDef = new GridDefinition();
            infoGridDef.Rows.AddRange(1f);
            infoGridDef.Columns.AddRange(48, 1f, 64);
            infoGridDef.CreateAutoCells();

            outerGridDef = new GridDefinition();
            outerGridDef.Rows.AddRange(1.5f, 72, 4f, 15f, 0f);
            outerGridDef.Columns.AddRange(1f);
            outerGridDef.CreateAutoCells();
        }

        public Calculator()
            : base()
        {
            InitializePage();

        }

        protected override Shape CreateDefaultBackground()
        {
            return Color.FromArgb(32, 32, 32);
        }

        private void InitializePage()
        {
            if (Toolbar != null) Toolbar.Dispose();
            Toolbar = null;
            SetTransition(400, TransitionMask.Zoom | TransitionMask.Fade, TransitionMask.Zoom | TransitionMask.Fade);
            //Controls.Add(new GradientControl(Color.DarkSlateBlue));

            ToolbarBackground = PhoneColors.MediumDark;
            infoGrid = new AnimationGrid { Definition = infoGridDef, Margin = Margin.Zero };
            buttonGrid = new AnimationGrid { Definition = buttonGridDef, Margin = Margin.Zero, GridState = startButtonState };
            outerGrid = new AnimationGrid { Definition = outerGridDef, Margin = Margin.Zero };
            Controls.Add(outerGrid);

            outerGrid.AddControl(1, infoGrid);
            outerGrid.AddControl(3, buttonGrid);

            rotate = new RotateTransform { Axis = RotateAxis.XAxis, Angle = DefaultAngle };
            buttonGrid.Transformation = rotate;


            numberBox = new TextBox
            {
                ReadOnly = true,
                Margin = Margin.Zero,
                Font = FontSprite.Bold,
                Background = new RectBorderFillShape { Color = Color.White, BorderColor = Color.Black },
                Chrome = Shape.Empty
            };

            outputBox = new TextBox
            {
                Margin = new Margin(4, 0, 0, 0),
                Enabled = true,
                ReadOnly = true,
                Background = new RectBorderFillShape { Color = Color.White, BorderColor = Color.Black },
                Chrome = Shape.Empty
            };

            Button box = new Button
            {
                Margin = new Margin(0, 3, 0, 3),
                Background = new RectBorderFillShape { BorderColor = Color.White, Color = Color.Empty },
                Chrome = Shape.Empty,
                Content = SysImages.Back,
                Style = MetroButtonStyle.Instance,
                TextColor = Color.White
            };
            box.Tap += (s, e) => { if (Text.Length > 0) Text = Text.Substring(0, Text.Length - 1); };
            infoGrid.AddControl(0, outputBox);
            infoGrid.AddControl(1, numberBox);
            infoGrid.AddControl(2, box);

            for (int i = 0; i < buttonsText.Length; i++)
            {
                int colorIndex = colorIndexes[i];
                Color c = buttonColors[colorIndex];
                Color textColor = textColors[colorIndex];
                CalcButton b = new CalcButton { Margin = new Margin(4), Color = c, Content = buttonsText[i], TextColor = textColor };
                b.WaveOffset = Angles[i] * 16;
                b.Tap += (s, e) => ButtonClick((Button)s);
                b.DoubleTap += (s, e) => ButtonClick((Button)s);
                buttonGrid.AddControl(i, b);
                buttons.Add(b);
            }


            Text = "0";
        }

        #endregion
        #region props

        public double Value
        {
            get
            {
                try
                {
                    return double.Parse(numberBox.Text);
                }
                catch
                {
                    return 0f;
                }
            }
            set
            {
                numberBox.Text = value.ToString();
            }
        }

        public string Text
        {
            get { return numberBox.Text; }
            set { numberBox.Text = value; }
        }

        #endregion
        #region methods

        protected override void OnHiding()
        {
            Screen.Tick -= BounceEvent;
            base.OnHiding();
        }

        protected override void Dispose(bool dispose)
        {
            Screen.Tick -= BounceEvent;
            base.Dispose(dispose);
        }


        protected override void OnHidden()
        {
            buttonGrid.GridState = startButtonState;
            base.OnHidden();
        }

        /// <summary>
        /// Before the calculator shows up, we randomize the grid states for animation effects:
        /// </summary>
        protected override void OnShowing()
        {
            Random r = new Random();
            for (int i = 0; i < 4 * 6; i++)
            {
                int mode = r.Next(3);
                GridCell cell = startButtonState.Cells[i];
                cell.RotateY = cell.RotateX = cell.RotateZ = 0;
                switch (mode)
                {
                    case 0: cell.RotateZ = 270; break;
                    case 1: cell.RotateY = 270; break;
                    default: cell.RotateX = 270; break;
                }
                cell.Minimized = true;
                cell.ResizeMode = CellResizeMode.Stretched;
                cell = buttonGridDef.Cells[i];
                cell.ResizeMode = CellResizeMode.Stretched;
                cell.StartDelay = r.Next(AnimationGrid.MaxLevel / 5);
                cell.StopDelay = r.Next(AnimationGrid.MaxLevel / 3);
            }

            base.OnShowing();
            //ShowButtonsAnimated();
        }

        /// <summary>
        /// when the calculator window has changed it's transition to become visible, we change the state of the button grid with a transition:
        /// </summary>
        protected override void OnShown()
        {
            base.OnShown();
            ShowButtonsAnimated();
        }

        private void ShowButtonsAnimated()
        {
            buttonGrid.ChangeState(buttonGridDef, 1000);

            amplitude = 80;
            Screen.Tick += BounceEvent;
        }


        void BounceEvent(object sender, Silvermoon.Core.TimeEventArgs e)
        {
            if (amplitude == 0d) return;
            int tick = e.Tick;

            foreach (CalcButton b in buttons)
            {
                double angle = (double)((tick + b.WaveOffset) % 360);
                float z = (float)(Math.Sin((Math.PI * angle / 360d)) * amplitude);
                b.ZOffset = z;
            }
            amplitude *= 0.90d;
            if (amplitude < 0.05d) amplitude = 0d;
        }


        private void ButtonClick(Button b)
        {
            string text = (b.Content as StringShape).Text;
            char c = text.Length == 1 ? text[0] : '\n';
            if ("0789456123.,".IndexOf(c) >= 0)
            {
                if (clear) Text = "";
                clear = false;
                if (c != ',' || numberBox.Text.IndexOf(',') < 0)
                {
                    if (Text == "0") Text = c != '.' ? c.ToString() : "0."; else Text += c;
                }
            }
            else
            {
                switch (text)
                {
                    case "MC":
                        TurnAround();
                        break;

                    case "C":
                        Value = 0f;
                        operation = Operation.Set;
                        calc = 0d;
                        break;

                    case "√":
                        Value = Math.Sqrt(Value);
                        break;

                    case "1/x":
                        if (Value != 0f)
                        {
                            Value = 1 / Value;
                        }
                        else numberBox.Text = "ERROR";
                        break;

                    case "±":
                    case "+/-":
                        Value = -Value;

                        break;

                    case "+":
                        Calculate();
                        operation = Operation.Plus;
                        break;

                    case "-":
                        Calculate();
                        operation = Operation.Minus;
                        break;

                    case "x":
                        Calculate();
                        operation = Operation.Multiply;
                        break;

                    case "÷":
                    case "/":
                        Calculate();
                        operation = Operation.Divide;
                        break;

                    case "=":
                        if (clear)
                        {
                            clear = false;
                            Value = current;
                        }
                        Calculate();
                        break;

                }
                clear = true;
            }
        }

        private void TurnAround()
        {
            //Animation a = new Animation(1100 * 3, DefaultAngle, 360 + DefaultAngle, 0) { EasingFunction = new BackEase { Mode = EasingMode.EaseInOut, Amplitude = 0.75d } };
            //a.Progress += (s, e) => { rotate.Angle = e.Value; buttonGrid.Invalidate(InvalidationFlag.Appearance); };
            //a.InvokeAsync();
        }


        /// <summary>
        /// Calculates this instance.
        /// </summary>
        private void Calculate()
        {
            double v = clear ? current : Value;
            if (!clear)
            {
                switch (operation)
                {
                    case Operation.Set: calc = v; break;
                    case Operation.Plus: calc = calc + v; break;
                    case Operation.Minus: calc = calc - v; break;
                    case Operation.Multiply: calc = calc * v; break;
                    case Operation.Divide: Divide(calc, v); break;
                }
            }

            current = v;
            Value = calc;
        }

        private void Divide(double last, double v)
        {
            if (v == 0f)
            {
                numberBox.Text = "DIVIDE BY ZERO";
            }
            else calc = last / v;
        }

        #endregion
        #region IMouseEventBehavior Members



        /// <summary>
        /// Let the buttons bounce when the window is tapped:
        /// </summary>
        protected override void OnTouchDown(TouchEventArgs e)
        {
            if (!e.Handled)
            {
                amplitude = (amplitude + 70d) / 1.5d;
            }
        }

        #endregion
    }
}
